#include <linux/sysfs.h>

#include "devdrv_pci.h"
#include "devdrv_ctrl.h"
#include "devdrv_sysfs.h"

extern struct devdrv_ctrl ctrls[];
extern int snprintf_s(char *strDest, size_t destMax, size_t count, const char *format, ...);

static u32 devdrv_sysfs_get_devid_by_dev(struct device *dev)
{
    u32 i;

    if (dev == NULL) {
        devdrv_err("dev is null\n");
        return MAX_DEV_CNT;
    }
    for (i = 0; i < MAX_DEV_CNT; i++) {
        if (dev == ctrls[i].dev)
            return ctrls[i].dev_id;
    }

    devdrv_err("find devid failed!\n");

    return MAX_DEV_CNT;
}

int devdrv_sysfs_common_msg_send(u32 devid, void *data, u32 *real_out_len)
{
    return devdrv_common_msg_send(devid, data, DEVDRV_SYSFS_MSG_IN_BYTES, sizeof(struct devdrv_sysfs_msg), real_out_len,
        DEVDRV_COMMON_MSG_SYSFS);
}


ssize_t devdrv_sysfs_link_info_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    u32 devid;
    u32 real_out_len = 0;
    int ret;
    struct devdrv_sysfs_msg msg;

    msg.type = DEVDRV_SYSFS_LINK_INFO;

    devdrv_info("devdrv sysfs show link info\n");

    devid = devdrv_sysfs_get_devid_by_dev(dev);
    if (devid >= MAX_DEV_CNT) {
        devdrv_err("get devid faild, devid:%d", devid);
        return 0;
    }

    ret = devdrv_sysfs_common_msg_send(devid, (void *)&msg, &real_out_len);
    if (ret) {
        devdrv_err("common_msg_send ERROR\n");
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "read link_info fail, ret:%d\n", ret);
        return ret == -1 ? 0 : ret;
    }

    ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "link speed: %d, link width: %d, link_status: 0x%x\n",
        msg.link_info.link_speed, msg.link_info.link_width, msg.link_info.link_status);
    return ret == -1 ? 0 : ret;
}

ssize_t devdrv_sysfs_rx_para_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    u32 devid;
    u32 real_out_len = 0;
    u32 offset = 0;
    u32 i;
    int ret;
    struct devdrv_sysfs_msg msg;

    msg.type = DEVDRV_SYSFS_RX_PARA;

    devdrv_info("devdrv sysfs show rx para\n");

    devid = devdrv_sysfs_get_devid_by_dev(dev);

    if (devid >= MAX_DEV_CNT) {
        devdrv_err("get devid faild, devid:%d\n", devid);
        return 0;
    }

    devdrv_info("get devid success, devid:%d\n", devid);

    ret = devdrv_sysfs_common_msg_send(devid, (void *)&msg, &real_out_len);
    if (ret) {
        devdrv_err("common_msg_send ERROR\n");
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "read rx_para fail, ret:%d\n", ret);
        if (ret != -1) {
            offset = ret;
        }
        return offset;
    }

    for (i = 0; i < DEVDRV_SYSFS_RX_LANE_MAX; i++) {
        ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1,
            "lane%d  att:%-10d gain:%-10d boost:%-10d tap1:%-10d tap2:%-10d\n", i, msg.rx_para.lane_rx_para[i].att,
            msg.rx_para.lane_rx_para[i].gain, msg.rx_para.lane_rx_para[i].boost, msg.rx_para.lane_rx_para[i].tap1,
            msg.rx_para.lane_rx_para[i].tap2);
        if (ret != -1) {
            offset += ret;
        }
    }

    return offset;
}


ssize_t devdrv_sysfs_tx_para_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    u32 devid;
    u32 real_out_len = 0;
    u32 offset = 0;
    u32 i;
    int ret;
    struct devdrv_sysfs_msg msg;

    msg.type = DEVDRV_SYSFS_TX_PARA;

    devdrv_info("devdrv sysfs show tx para\n");

    devid = devdrv_sysfs_get_devid_by_dev(dev);

    if (devid >= MAX_DEV_CNT) {
        devdrv_err("get devid faild, devid:%d\n", devid);
        return 0;
    }

    devdrv_info("get devid success, devid:%d\n", devid);

    ret = devdrv_sysfs_common_msg_send(devid, (void *)&msg, &real_out_len);
    if (ret) {
        devdrv_err("common_msg_send ERROR\n");
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "read tx_para fail, ret:%d\n", ret);
        if (ret != -1) {
            offset = ret;
        }
        return offset;
    }

    for (i = 0; i < DEVDRV_SYSFS_TX_LANE_MAX; i++) {
        ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1,
            "lane%d  pre:%-10d main:%-10d post:%-10d\n", i, msg.tx_para.lane_tx_para[i].pre,
            msg.tx_para.lane_tx_para[i].main, msg.tx_para.lane_tx_para[i].post);
        if (ret != -1) {
            offset += ret;
        }
    }

    return offset;
}


ssize_t devdrv_sysfs_aer_cnt_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    u32 devid;
    u32 real_out_len = 0;
    int ret;
    struct devdrv_sysfs_msg msg;

    msg.type = DEVDRV_SYSFS_AER_COUNT;

    devdrv_info("devdrv sysfs show aer count\n");

    devid = devdrv_sysfs_get_devid_by_dev(dev);

    if (devid >= MAX_DEV_CNT) {
        devdrv_err("get devid faild, devid:%d", devid);
        return 0;
    }

    devdrv_info("get devid success, devid:%d", devid);

    ret = devdrv_sysfs_common_msg_send(devid, (void *)&msg, &real_out_len);
    if (ret) {
        devdrv_err("common_msg_send ERROR\n");
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "read aer count fail, ret:%d\n", ret);
        return ret == -1 ? 0 : ret;
    }

    ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "AER count: %d\n", msg.data[0]);
    return ret == -1 ? 0 : ret;
}

ssize_t devdrv_sysfs_aer_cnt_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
    u32 devid;
    u32 real_out_len = 0;
    int ret;
    struct devdrv_sysfs_msg msg;

    msg.type = DEVDRV_SYSFS_AER_CLEAR;

    devdrv_info("devdrv sysfs clear aer count\n");

    devid = devdrv_sysfs_get_devid_by_dev(dev);

    if (devid >= MAX_DEV_CNT) {
        devdrv_err("get devid faild, devid:%d", devid);
        return 0;
    }

    devdrv_info("get devid success, devid:%d", devid);

    if (!strcmp(buf, "clear")) {
        ret = devdrv_sysfs_common_msg_send(devid, (void *)&msg, &real_out_len);
        if (ret) {
            devdrv_err("common_msg_send ERROR\n");
        }
    }

    return count;
}

ssize_t devdrv_sysfs_bdf_to_devid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct pci_dev *pdev = NULL;
    ssize_t offset = 0;
    u8 bus_num = 0;
    u8 device_num = 0;
    u8 func_num = 0;
    int ret;
    u32 i;

    devdrv_info("devdrv sysfs show bdf and device id\n");

    for (i = 0; i < MAX_DEV_CNT; i++) {
        if ((ctrls[i].dev_id < MAX_DEV_CNT) && (ctrls[i].pdev != NULL) &&
            (ctrls[i].startup_flg != DEVDRV_DEV_STARTUP_UNPROBED)) {
            pdev = ctrls[i].pdev;

            bus_num = pdev->bus->number;
            device_num = PCI_SLOT(pdev->devfn);
            func_num = PCI_FUNC(pdev->devfn);
            ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1, "%02x:%02x.%d ---> %d\n",
                bus_num, device_num, func_num, ctrls[i].dev_id);
            if (ret != -1) {
                offset += ret;
            }
        }
    }

    return offset;
}

ssize_t devdrv_sysfs_bios_version_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    struct devdrv_pci_ctrl *pci_ctrl = NULL;
    int i = 0;
    ssize_t offset = 0;
    int ret = 0;

    for (i = 0; i < MAX_DEV_CNT; i++) {
        if ((ctrls[i].dev_id < MAX_DEV_CNT) && (ctrls[i].dev == dev)) {
            pci_ctrl = (struct devdrv_pci_ctrl *)ctrls[i].priv;
            break;
        }
    }
    if (pci_ctrl == NULL) {
        devdrv_err("get pci_ctrl failed\n");
        return 0;
    }

    if (pci_ctrl->device_bios_version.valid == false) {
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "device_fw_unsupported\n");
        if (ret != -1) {
            offset = ret;
        }
        return offset;
    }

    ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1, "bootrom:%x.%x.%x.%x\n",
        pci_ctrl->device_bios_version.bootrom[0], pci_ctrl->device_bios_version.bootrom[1],
        pci_ctrl->device_bios_version.bootrom[2], pci_ctrl->device_bios_version.bootrom[3]);
    if (ret != -1) {
        offset += ret;
    }

    ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1, "xloader:%x.%x.%x.%05x\n",
        pci_ctrl->device_bios_version.xloader[0], pci_ctrl->device_bios_version.xloader[1],
        pci_ctrl->device_bios_version.xloader[2], pci_ctrl->device_bios_version.xloader[3]);
    if (ret != -1) {
        offset += ret;
    }

    ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1, "nve:%05x\n",
        pci_ctrl->device_bios_version.nve);
    if (ret != -1) {
        offset += ret;
    }

    return offset;
}

ssize_t devdrv_sysfs_hdc_common_msg_show(struct device *dev, struct device_attribute *attr, char *buf)
{
    u32 devid;
    u32 real_out_len = 0;
    int ret, i;
    struct devdrv_sysfs_msg msg;
    ssize_t offset = 0;
    struct devdrv_pci_ctrl *pci_ctrl = NULL;
    struct devdrv_msg_dev *msg_dev = NULL;

    for (i = 0; i < MAX_DEV_CNT; i++) {
        if ((ctrls[i].dev_id < MAX_DEV_CNT) && (ctrls[i].dev == dev)) {
            pci_ctrl = (struct devdrv_pci_ctrl *)ctrls[i].priv;
            break;
        }
    }
    if (pci_ctrl == NULL) {
        devdrv_err("common_msg_info_show get pci_ctrl failed\n");
        return 0;
    }

    msg_dev = pci_ctrl->msg_dev;
    devid = pci_ctrl->dev_id;

    devdrv_info("devdrv sysfs show common msg info\n");
    ret = memset_s(&msg, sizeof(struct devdrv_sysfs_msg), 0, sizeof(struct devdrv_sysfs_msg));
    if (ret != 0) {
        devdrv_err("memset_s failed\n");
        return 0;
    }
    msg.type = DEVDRV_SYSFS_HDC_COMMON_MSG_INFO;

    ret = devdrv_sysfs_common_msg_send(devid, (void *)&msg, &real_out_len);
    if (ret) {
        devdrv_err("common_msg_send ERROR\n");
        ret = snprintf_s(buf, PAGE_SIZE, PAGE_SIZE - 1, "read aer count fail, ret:%d\n", ret);
        return (ret == -1) ? 0 : ret;
    }

    ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1,
        "---host---\n isr_trigger:%u, isr_in_front_dma:%u\n work_in:%u, work_after_sleep:%u, no_data:%u\n",
        msg_dev->hdc_common_msg_dfx.isr_cnt, msg_dev->hdc_common_msg_dfx.isr_in_front_dma_cnt,
        msg_dev->hdc_common_msg_dfx.work_in_cnt, msg_dev->hdc_common_msg_dfx.work_after_sleep_cnt,
        msg_dev->hdc_common_msg_dfx.work_no_data_cnt);
    if (ret != -1) {
        offset += ret;
    }

    ret = snprintf_s(buf + offset, PAGE_SIZE - offset, PAGE_SIZE - offset - 1,
        "---device---\n hdc_send:%u, dma_fail:%u, suc:%u\n time out:%u, fail:%u\n", msg.data[0], msg.data[1],
        msg.data[2], msg.data[3], msg.data[4]);
    if (ret != -1) {
        offset += ret;
    }

    return offset;
}


static DEVICE_ATTR(devdrv_sysfs_link_info, S_IRUGO, devdrv_sysfs_link_info_show, NULL);
static DEVICE_ATTR(devdrv_sysfs_rx_para_info, S_IRUGO, devdrv_sysfs_rx_para_show, NULL);
static DEVICE_ATTR(devdrv_sysfs_tx_para_info, S_IRUGO, devdrv_sysfs_tx_para_show, NULL);
static DEVICE_ATTR(devdrv_sysfs_aer_cnt_info, S_IRUGO | S_IWUSR | S_IWGRP, devdrv_sysfs_aer_cnt_show,
    devdrv_sysfs_aer_cnt_store);
static DEVICE_ATTR(devdrv_sysfs_bdf_to_devid, S_IRUGO, devdrv_sysfs_bdf_to_devid_show, NULL);
static DEVICE_ATTR(devdrv_sysfs_bios_version, S_IRUGO, devdrv_sysfs_bios_version_show, NULL);
static DEVICE_ATTR(devdrv_sysfs_hdc_common_msg, S_IRUGO, devdrv_sysfs_hdc_common_msg_show, NULL);


static struct attribute *devdrv_sysfs_attrs[] = {
    &dev_attr_devdrv_sysfs_link_info.attr,
    &dev_attr_devdrv_sysfs_rx_para_info.attr,
    &dev_attr_devdrv_sysfs_tx_para_info.attr,
    &dev_attr_devdrv_sysfs_aer_cnt_info.attr,
    &dev_attr_devdrv_sysfs_bdf_to_devid.attr,
    &dev_attr_devdrv_sysfs_bios_version.attr,
    &dev_attr_devdrv_sysfs_hdc_common_msg.attr,
    NULL,
};

static const struct attribute_group devdrv_sysfs_group = {
    .attrs = devdrv_sysfs_attrs,
};

int devdrv_sysfs_init(struct devdrv_pci_ctrl *pci_ctrl)
{
    return sysfs_create_group(&pci_ctrl->pdev->dev.kobj, &devdrv_sysfs_group);
}

void devdrv_sysfs_exit(struct devdrv_pci_ctrl *pci_ctrl)
{
    sysfs_remove_group(&pci_ctrl->pdev->dev.kobj, &devdrv_sysfs_group);
}
